package session.stateless;

import entity.BatchRequired;
import entity.Employee;
import entity.Product;
import entity.ProductionBatch;
import entity.ProductionSchedule;
import entity.ProductionScheduleDay;
import entity.PurchaseItem;
import entity.Warehouse;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashSet;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

@Stateless
public class ProductionScheduleSessionBean implements ProductionScheduleSessionBeanLocal {

    @PersistenceContext
    private EntityManager entityManager;
    @EJB
    private ProductionBatchSessionBeanLocal productionBatchSessionBeanLocal;
    @EJB
    private PurchaseOrderSessionBeanLocal purchaseOrderSessionBeanLocal;

    @Override
    public void removeProductionSchedule(Integer month, Integer year) {
        ArrayList<ProductionSchedule> productionSchedules = getProductionSchedules(month, year);
        for (ProductionSchedule ps : productionSchedules) {
            ProductionSchedule remove_ps = entityManager.find(ProductionSchedule.class, ps.getId());
            for (ProductionScheduleDay psd : remove_ps.getProductionScheduleDays()) {
                ProductionScheduleDay productionScheduleDay = entityManager.find(ProductionScheduleDay.class, psd.getId());
                productionScheduleDay.setProduct(null);
                entityManager.flush();
            }
            remove_ps.setEmployee(null);
            entityManager.persist(remove_ps);
            entityManager.flush();
            entityManager.remove(remove_ps);
            entityManager.flush();
        }


    }

    @Override
    public void removeProductionScheduleDays(String status) {
        ArrayList<ProductionScheduleDay> productionScheduleDays = getProductionScheduleDays(status);

        for (ProductionScheduleDay psd : productionScheduleDays) {
            ProductionScheduleDay productionScheduleDay = entityManager.find(ProductionScheduleDay.class, psd.getId());
            productionScheduleDay.setProduct(null);
            productionScheduleDay.setPurchaseItems(null);
            productionScheduleDay.setProductionSchedule(null);
            entityManager.flush();
            entityManager.remove(productionScheduleDay);
        }
    }

    @Override
    public void addProductionSchedule(ProductionSchedule productionSchedule) {

        for (ProductionScheduleDay psd : productionSchedule.getProductionScheduleDays()) {
            Product product = entityManager.find(Product.class, psd.getProduct().getCode());
            psd.setProduct(product);

            if (psd.getPurchaseItems() != null) {
                ArrayList<PurchaseItem> pois = new ArrayList<PurchaseItem>();
                for (PurchaseItem poi : psd.getPurchaseItems()) {
                    PurchaseItem purchaseItem = entityManager.find(PurchaseItem.class, poi.getId());
                    pois.add(purchaseItem);
                }
                psd.setPurchaseItems(pois);
            }
        }
        Employee employee = entityManager.find(Employee.class, productionSchedule.getEmployee().getUserId());
        productionSchedule.setEmployee(employee);
        entityManager.persist(productionSchedule);
        entityManager.flush();
    }

    @Override
    public ArrayList<String> getPhs(String url) {
        ArrayList<String> phs = new ArrayList<String>();


        try {

            InputStreamReader stream = new InputStreamReader((new URL(url)).openStream());
            BufferedReader br = new BufferedReader(stream);

            String s;
            while ((s = br.readLine()) != null) {
                phs.add(s);
            }
            stream.close();
        } catch (Exception ex) {
            System.out.println(ex.toString());
        }

        return phs;
    }

    @Override
    public ArrayList<ProductionSchedule> getProductionSchedules(Integer month, Integer year) {
        ArrayList<ProductionSchedule> productionSchedules = new ArrayList<ProductionSchedule>();

        Query query = entityManager.createQuery("SELECT u FROM ProductionSchedule u WHERE u.operationMonth=:inMonth AND u.operationYear=:inYear");
        query.setParameter("inMonth", month);
        query.setParameter("inYear", year);

        for (Object o : query.getResultList()) {
            ProductionSchedule productionSchedule = (ProductionSchedule) o;
            productionSchedules.add(productionSchedule);
        }
        return productionSchedules;
    }

    @Override
    public ArrayList<ProductionScheduleDay> getProductionScheduleDays(String type) {
        ArrayList<ProductionScheduleDay> productionScheduleDays = new ArrayList<ProductionScheduleDay>();

        Query query = entityManager.createQuery("SELECT u FROM ProductionScheduleDay u WHERE u.type=:inType");
        query.setParameter("inType", type);
        for (Object o : query.getResultList()) {
            ProductionScheduleDay productionScheduleDay = (ProductionScheduleDay) o;
            productionScheduleDays.add(productionScheduleDay);
        }
        return productionScheduleDays;
    }

    @Override
    public void removePurchaseItemFromPSD(Long productionScheduleDayId, Long purchaseItemId) {
        ProductionScheduleDay psd = entityManager.find(ProductionScheduleDay.class, productionScheduleDayId);
        PurchaseItem poi = entityManager.find(PurchaseItem.class, purchaseItemId);
        psd.getPurchaseItems().remove(poi);
        entityManager.persist(psd);
        entityManager.flush();
    }

    @Override
    public void addPurchaseItemToPSD(Long productionScheduleDayId, Long purchaseItemId, Boolean isBackOrder) {
        ProductionScheduleDay psd = entityManager.find(ProductionScheduleDay.class, productionScheduleDayId);
        PurchaseItem poi = entityManager.find(PurchaseItem.class, purchaseItemId);

        psd.getPurchaseItems().add(poi);
        psd.setReservedQuantity(psd.getReservedQuantity() + poi.getQuantity());

        if (isBackOrder) {
            psd.setQuantity(psd.getQuantity() + poi.getQuantity());
        }
        entityManager.persist(psd);
        entityManager.flush();
    }

    @Override
    public void addBackOrder(ProductionScheduleDay psd) {
        System.out.println(psd.getQuantity());
        ProductionScheduleDay productionScheduleDay = new ProductionScheduleDay(null, psd.getCurrentDay(), null, psd.getType(), psd.getQuantity(), psd.getReservedQuantity(), psd.getUtilizedHours());
        Product product = entityManager.find(Product.class, psd.getProduct().getCode());
        productionScheduleDay.setProduct(product);
        entityManager.persist(productionScheduleDay);
        entityManager.flush();
    }

    @Override
    public void removePurchaseItemFromPSDs(Long purchaseItemId) {
        PurchaseItem poi = entityManager.find(PurchaseItem.class, purchaseItemId);
        ArrayList<ProductionScheduleDay> psds = new ArrayList<ProductionScheduleDay>();
        for (ProductionScheduleDay psd : getProductionScheduleDays("Normal")) {
            for (PurchaseItem pi : psd.getPurchaseItems()) {
                if (pi.getId().compareTo(poi.getId()) == 0) {
                    psds.add(psd);
                }
            }
        }

        HashSet hs = new HashSet(psds);
        psds.clear();
        psds.addAll(hs);

        for (ProductionScheduleDay psd : psds) {
            ProductionScheduleDay productionScheduleDay = entityManager.find(ProductionScheduleDay.class, psd.getId());
            productionScheduleDay.getPurchaseItems().remove(poi);
            entityManager.persist(poi);
            entityManager.flush();
        }
    }

    @Override
    public Date checkDateFromPlan(String code, Integer quantity, Integer month, Integer year) {
        Integer count = quantity;
        ProductionScheduleDay productionScheduleDay = null;
        for (ProductionSchedule ps : getProductionSchedules(month, year)) {
            for (ProductionScheduleDay psd : ps.getProductionScheduleDays()) {
                if (psd.getProduct().getCode().compareTo(code) == 0) {
                    if (psd.getQuantity() - psd.getReservedQuantity() > 0) {
                        System.out.println(count);
                        count -= (psd.getQuantity() - psd.getReservedQuantity());
                        productionScheduleDay = psd;
                    }
                }

            }

        }

        if (count < 0 && productionScheduleDay != null) {
            Calendar cal = Calendar.getInstance();
            cal.set(productionScheduleDay.getProductionSchedule().getOperationYear(), productionScheduleDay.getProductionSchedule().getOperationMonth() - 1, productionScheduleDay.getCurrentDay());
            cal.add(cal.DAY_OF_MONTH, +7);
            return cal.getTime();
        }
        return null;


    }

    @Override
    public ArrayList<ProductionSchedule> getProductionSchedules() {
        ArrayList<ProductionSchedule> pss = new ArrayList<ProductionSchedule>();
        Query query = entityManager.createQuery("SELECT u FROM ProductionSchedule u");

        for (Object o : query.getResultList()) {
            ProductionSchedule ps = (ProductionSchedule) o;
            pss.add(ps);
        }


        ArrayList<ProductionSchedule> pss2 = new ArrayList<ProductionSchedule>();
        for (int i = 1; i < 13; i++) {
            for (ProductionSchedule ps : pss) {
                if (ps.getOperationMonth().compareTo(i) == 0) {
                    pss2.add(ps);
                }
            }
        }


        return pss2;

    }

    @Override
    public void updateProductionScheduleDay(Long psdId, Integer reservedQuantity) {
        //when production batch is produced
        ProductionScheduleDay psd = entityManager.find(ProductionScheduleDay.class, psdId);
        psd.setReservedQuantity(reservedQuantity);
        entityManager.persist(psd);
        entityManager.flush();

    }

    @Override
    public Boolean ATPCheck(Warehouse wh, ArrayList<PurchaseItem> purchasedItems, ArrayList<ProductionSchedule> productionSchedules) {
        ArrayList<ProductionBatch> productionBatches = new ArrayList(wh.getProductionBatches());
        ArrayList<PurchaseItem> purchaseItems = purchasedItems;

        ArrayList<ProductionSchedule> pss = productionSchedules;
        Calendar cal = Calendar.getInstance(); //request date
        Calendar cal2 = Calendar.getInstance();//pb or psd date


        for (PurchaseItem poi : purchaseItems) {
            cal.setTime(poi.getRequestDate());
            cal.add(cal.DAY_OF_MONTH, -7);  //7 days buffer for delivery
            int count = poi.getQuantity();
            System.out.println("CHECK POI: " + poi.getId() + " date " + poi.getRequestDate() + poi.getProduct().getName());

            for (ProductionBatch pb : productionBatches) {
                if (poi.getQuantity() > 0) {

                    cal2.set(pb.getProductionScheduleDay().getProductionSchedule().getOperationYear(),
                            pb.getProductionScheduleDay().getProductionSchedule().getOperationMonth() - 1,
                            pb.getProductionScheduleDay().getCurrentDay());
                    if (cal.before(cal2)) //request date is before current date & is unfufilled
                    {
                        System.out.println("DEAD on " + pb.getProductionScheduleDay().getCurrentDay() + "/" + pb.getProductionScheduleDay().getProductionSchedule().getOperationMonth() + "/" + pb.getProductionScheduleDay().getProductionSchedule().getOperationYear());
                        System.out.println("KILLED BY PB: " + pb.getId());
                        return false;
                    } else {

                        if (poi.getProduct().getCode().compareTo(pb.getProduct().getCode()) == 0
                                && (pb.getQuantity() - pb.getUtilizedQuantity() > 0)) {
                            //satisfy partial
                            if (count > (pb.getQuantity() - pb.getUtilizedQuantity())) {
                                System.out.println("PB : TAKE " + (pb.getQuantity() - pb.getUtilizedQuantity()) + "FROM " + pb.getProductionScheduleDay().getCurrentDay()
                                        + "/" + pb.getProductionScheduleDay().getProductionSchedule().getOperationMonth() + "/" + pb.getProductionScheduleDay().getProductionSchedule().getOperationYear());

                                count -= (pb.getQuantity() - pb.getUtilizedQuantity());
                                pb.setUtilizedQuantity(pb.getQuantity());   //take all
                            } else {
                                System.out.println("KILL " + cal2.getTime());
                                System.out.println("PB : TAKE " + count + " FROM " + pb.getProductionScheduleDay().getCurrentDay() + "/"
                                        + pb.getProductionScheduleDay().getProductionSchedule().getOperationMonth() + "/" + pb.getProductionScheduleDay().getProductionSchedule().getOperationYear() + "<- LAST");
                                pb.setUtilizedQuantity(pb.getUtilizedQuantity() + count);
                                count = 0;
                            }

                        }
                    }
                }   //finish checking from inventory
            }

            if (count > 0) {
                System.out.println("   ");
                System.out.println("PRODUCTION SCHEDULE");
                for (ProductionSchedule ps : pss) {
                    if (count > 0) //another month if there is still unfulfilled
                    {
                        for (ProductionScheduleDay psd : ps.getProductionScheduleDays()) //check another 
                        {
                            if (count > 0) //check another day if there is still unfulfilled
                            {

                                cal2.set(psd.getProductionSchedule().getOperationYear(),
                                        psd.getProductionSchedule().getOperationMonth() - 1,
                                        psd.getCurrentDay());

                                if (cal.before(cal2)) {
                                    System.out.println("DEAD on " + psd.getCurrentDay() + "/" + psd.getProductionSchedule().getOperationMonth() + "/" + psd.getProductionSchedule().getOperationYear());
                                    System.out.println("KILLED BY PSD: " + psd.getId());
                                    return false;
                                } else {
                                    if (psd.getProduct().getCode().compareTo(poi.getProduct().getCode()) == 0
                                            && (psd.getQuantity() - psd.getReservedQuantity()) > 0) {
                                        //          Plan take partial
                                        if (count > (psd.getQuantity() - psd.getReservedQuantity())) {
                                            System.out.println("PSD TAKE " + (psd.getQuantity() - psd.getReservedQuantity())
                                                    + "FROM " + psd.getCurrentDay() + "/" + psd.getProductionSchedule().getOperationMonth() + "/"
                                                    + psd.getProductionSchedule().getOperationYear() + " Product " + psd.getProduct().getName());

                                            count -= (psd.getQuantity() - psd.getReservedQuantity());
                                            psd.setReservedQuantity(psd.getQuantity());
                                            System.out.println("LEFT " + (psd.getQuantity() - psd.getReservedQuantity()));

                                        } else {
                                            System.out.println("PSD TAKE " + count + " FROM " + psd.getCurrentDay() + "/"
                                                    + psd.getProductionSchedule().getOperationMonth() + "/"
                                                    + psd.getProductionSchedule().getOperationYear() + " Product " + psd.getProduct().getName() + "<- LAST");
                                            psd.setReservedQuantity(psd.getReservedQuantity() + count);
                                            count = 0;
                                            System.out.println("LEFT " + (psd.getQuantity() - psd.getReservedQuantity()));
                                        }
                                    }
                                }
                            }
                        }
                    }

                }

            }

        }


        return true;
    }

    @Override
    public ArrayList<Date> getUpdatedPurchaseItemsDates(Warehouse wh, ArrayList<PurchaseItem> purchasedItems, ArrayList<ProductionSchedule> productionSchedules) {
        ArrayList<ProductionBatch> productionBatches = new ArrayList(wh.getProductionBatches());
        ArrayList<PurchaseItem> purchaseItems = purchasedItems;
        ArrayList<ProductionSchedule> pss = productionSchedules;
        Calendar cal = Calendar.getInstance(); //request date  
        ArrayList<Date> returnDates = new ArrayList<Date>();

        for (PurchaseItem poi : purchaseItems) {
            cal.setTime(poi.getRequestDate());
            int count = poi.getQuantity();
            System.out.println("CHECK POI: " + poi.getId() + " date " + poi.getRequestDate() + poi.getProduct().getName());

            for (ProductionBatch pb : productionBatches) {
                if (poi.getQuantity() > 0) {

                    cal.set(pb.getProductionScheduleDay().getProductionSchedule().getOperationYear(),
                            pb.getProductionScheduleDay().getProductionSchedule().getOperationMonth() - 1,
                            pb.getProductionScheduleDay().getCurrentDay());

                    if (poi.getProduct().getCode().compareTo(pb.getProduct().getCode()) == 0
                            && (pb.getQuantity() - pb.getUtilizedQuantity() > 0)) {
                        //satisfy partial
                        if (count > (pb.getQuantity() - pb.getUtilizedQuantity())) {
                            System.out.println("PB : TAKE " + (pb.getQuantity() - pb.getUtilizedQuantity()) + "FROM " + pb.getProductionScheduleDay().getCurrentDay()
                                    + "/" + pb.getProductionScheduleDay().getProductionSchedule().getOperationMonth() + "/" + pb.getProductionScheduleDay().getProductionSchedule().getOperationYear());

                            count -= (pb.getQuantity() - pb.getUtilizedQuantity());
                            pb.setUtilizedQuantity(pb.getQuantity());   //take all
                        } else {
                            System.out.println("KILL " + cal.getTime());
                            System.out.println("PB : TAKE " + count + " FROM " + pb.getProductionScheduleDay().getCurrentDay() + "/"
                                    + pb.getProductionScheduleDay().getProductionSchedule().getOperationMonth() + "/" + pb.getProductionScheduleDay().getProductionSchedule().getOperationYear() + "<- LAST");
                            pb.setUtilizedQuantity(pb.getUtilizedQuantity() + count);
                            cal.add(Calendar.DAY_OF_MONTH, 14);
                            System.out.println("PROPOSE DATE:" + cal.getTime());
                            returnDates.add(cal.getTime());
                            count = 0;
                        }

                    }
                }
            }   //finish checking from inventory

            if (count > 0) {
                System.out.println("   ");
                System.out.println("PRODUCTION SCHEDULE");
                for (ProductionSchedule ps : pss) {
                    if (count > 0) //another month if there is still unfulfilled
                    {
                        for (ProductionScheduleDay psd : ps.getProductionScheduleDays()) //check another 
                        {
                            if (count > 0) //check another day if there is still unfulfilled
                            {

                                cal.set(psd.getProductionSchedule().getOperationYear(),
                                        psd.getProductionSchedule().getOperationMonth() - 1,
                                        psd.getCurrentDay());


                                if (psd.getProduct().getCode().compareTo(poi.getProduct().getCode()) == 0
                                        && (psd.getQuantity() - psd.getReservedQuantity()) > 0) {
                                    //          Plan take partial
                                    if (count > (psd.getQuantity() - psd.getReservedQuantity())) {
                                        System.out.println("PSD TAKE " + (psd.getQuantity() - psd.getReservedQuantity())
                                                + "FROM " + psd.getCurrentDay() + "/" + psd.getProductionSchedule().getOperationMonth() + "/"
                                                + psd.getProductionSchedule().getOperationYear() + " Product " + psd.getProduct().getName());

                                        count -= (psd.getQuantity() - psd.getReservedQuantity());
                                        psd.setReservedQuantity(psd.getQuantity());
                                        System.out.println("LEFT " + (psd.getQuantity() - psd.getReservedQuantity()));

                                    } else {
                                        System.out.println("PSD TAKE " + count + " FROM " + psd.getCurrentDay() + "/"
                                                + psd.getProductionSchedule().getOperationMonth() + "/"
                                                + psd.getProductionSchedule().getOperationYear() + " Product " + psd.getProduct().getName() + "<- LAST");
                                        psd.setReservedQuantity(psd.getReservedQuantity() + count);
                                        System.out.println("LEFT " + (psd.getQuantity() - psd.getReservedQuantity()));
                                        cal.add(Calendar.DAY_OF_MONTH, 14);
                                        System.out.println("PROPOSE DATE:" + cal.getTime());
                                        returnDates.add(cal.getTime());
                                        count = 0;
                                    }
                                }
                            }
                        }
                    }
                }

            }

        }

        return returnDates;
    }

    @Override
    public void reservePurchaseItem(Warehouse wh, Long poId, ArrayList<ProductionSchedule> productionSchedules) {
        ArrayList<ProductionBatch> productionBatches = new ArrayList(wh.getProductionBatches());
        ArrayList<ProductionSchedule> pss = productionSchedules;
        
        Calendar cal = Calendar.getInstance(); //request date
        Calendar cal2 = Calendar.getInstance();//pb or psd date    
        
        PurchaseItem poi = entityManager.find(PurchaseItem.class, poId);
        Integer count = poi.getQuantity();

        cal.setTime(poi.getRequestDate());
        System.out.println(poi.getQuantity());
        System.out.println(
                "CHECK POI: " + poi.getId() + " date " + poi.getRequestDate());

        for (ProductionBatch pb : productionBatches) {
            if (count > 0) {
                cal2.set(pb.getProductionScheduleDay().getProductionSchedule().getOperationYear(),
                        pb.getProductionScheduleDay().getProductionSchedule().getOperationMonth() - 1,
                        pb.getProductionScheduleDay().getCurrentDay());

                if (poi.getProduct().getCode().compareTo(pb.getProduct().getCode()) == 0
                        && (pb.getQuantity() - pb.getUtilizedQuantity() > 0)) {
                    //Have leftover satisfy partial
                    if (count > (pb.getQuantity() - pb.getUtilizedQuantity())) {
                        System.out.println("PB : TAKE " + (pb.getQuantity() - pb.getUtilizedQuantity()) + "FROM " + pb.getProductionScheduleDay().getCurrentDay()
                                + "/" + pb.getProductionScheduleDay().getProductionSchedule().getOperationMonth() + "/" + pb.getProductionScheduleDay().getProductionSchedule().getOperationYear());

                        BatchRequired br = new BatchRequired(pb.getQuantity() - pb.getUtilizedQuantity(), pb);
                        count -= (pb.getQuantity() - pb.getUtilizedQuantity());
//                        poi.setQuantity(poi.getQuantity() - (pb.getQuantity() - pb.getUtilizedQuantity()));
                     //   poi.getBatchesRequired().add(br);
                        pb.setUtilizedQuantity(pb.getQuantity());
                        productionBatchSessionBeanLocal.updateProductionBatch(pb);
                        purchaseOrderSessionBeanLocal.addPurchaseItemBatch(poi.getId(), br);

                    } else {
                        System.out.println("KILL " + cal2.getTime());
                        System.out.println("PB : TAKE " + count + " FROM " + pb.getProductionScheduleDay().getCurrentDay() + "/"
                                + pb.getProductionScheduleDay().getProductionSchedule().getOperationMonth() + "/" + pb.getProductionScheduleDay().getProductionSchedule().getOperationYear() + "<- LAST");

                        BatchRequired br = new BatchRequired(count, pb);
                        //poi.getBatchesRequired().add(br);
                        purchaseOrderSessionBeanLocal.addPurchaseItemBatch(poi.getId(), br);
                        pb.setUtilizedQuantity(pb.getUtilizedQuantity() + count);
                        productionBatchSessionBeanLocal.updateProductionBatch(pb);
                        count = 0;
                    }
                }
            }
        }

        if (count
                > 0) {
            for (ProductionSchedule ps : pss) {
                if (count > 0) {
                    for (ProductionScheduleDay psd : ps.getProductionScheduleDays()) {
                        if (count > 0) {

                            cal2.set(psd.getProductionSchedule().getOperationYear(),
                                    psd.getProductionSchedule().getOperationMonth() - 1,
                                    psd.getCurrentDay());

                            if (count > 0 && (psd.getQuantity() - psd.getReservedQuantity()) > 0
                                    && psd.getProduct().getCode().compareTo(poi.getProduct().getCode()) == 0) {
                                if (count > (psd.getQuantity() - psd.getReservedQuantity())) //Plan take partial
                                {

                                    System.out.println("PSD TAKE " + (psd.getQuantity() - psd.getReservedQuantity()) + "FROM " + psd.getCurrentDay() + "/" + psd.getProductionSchedule().getOperationMonth() + psd.getProductionSchedule().getOperationYear());
                                    this.addPurchaseItemToPSD(psd.getId(), poi.getId(), false);
                                    this.updateProductionScheduleDay(psd.getId(), psd.getQuantity());                            //reserve == quantity                    
                                    //poi.setQuantity(poi.getQuantity() - (psd.getQuantity() - psd.getReservedQuantity()));
                                    count -= (psd.getQuantity() - psd.getReservedQuantity());
                                } else //fully satisfy
                                {
                                    System.out.println("PSD TAKE " + count + "FROM " + psd.getCurrentDay() + "/" + psd.getProductionSchedule().getOperationMonth() + psd.getProductionSchedule().getOperationYear() + "<- LAST");
                                    this.addPurchaseItemToPSD(psd.getId(), poi.getId(), false);
                                    this.updateProductionScheduleDay(psd.getId(), psd.getReservedQuantity() + count);
                                    count = 0;
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
